from flask import current_app, jsonify, g
from sqlalchemy import desc, asc

from apps.interface.business.interfacecasedata import InterfaceCaseDataBusiness
from apps.interface.business.interfacecaserelation import InterfaceCaseRelationBusiness
from apps.interface.business.interfaceapimsg import InterfaceApiMsgBusiness

from apps.interface.models.interfaceapimsg import InterfaceApiMsg
from apps.interface.models.interfacecasedata import InterfaceCaseData
from apps.interface.models.interfacecaseset import InterfaceCaseSet
from apps.interface.models.interfacecase import InterfaceCase
from apps.interface.models.interfaceproject import InterfaceProject
from apps.interface.models.interfacecaserelation import InterfaceCaseRelation
from apps.interface.util.utils import *
from apps.interface.util.http_run import RunCase
from apps.interface.util.utils import extract_variables, convert, check_case, auto_num, num_sort
from library.api.db import db
from library.api.transfer import transfer2json
from apps.auth.extentions import parse_pwd


class InterfaceCaseBusiness(object):
    
    @classmethod
    def _query(cls):
        return InterfaceCase.query.add_columns(
            InterfaceCase.id.label('id'),
            InterfaceCase.num.label('num'),
            InterfaceCase.name.label('name'),
            InterfaceCase.desc.label('desc'),
            InterfaceCase.func_address.label('func_address'),
            InterfaceCase.variable.label('variable'),
            InterfaceCase.times.label('times'),
            InterfaceCase.project_id.label('project_id'),
            InterfaceCase.priority.label('priority'),
            InterfaceCase.case_set_id.label('case_set_id'),
            InterfaceCase.status.label('status'),
        )
    
    @classmethod
    @transfer2json(
        '?id|!num|!name|!desc|!func_address|!variable|!times|!project_id|!case_set_id|!status')
    def query_all_json(cls, limit, offset):
        ret = cls._query().filter(InterfaceCase.status == InterfaceCase.ACTIVE) \
            .order_by(desc(InterfaceCase.id)) \
            .limit(limit).offset(offset).all()
        return ret
    
    @classmethod
    def case_create(cls, num, name, desc, func_address, variable, times, project_id, case_set_id,
                    priority, case_type):
        try:
            m = InterfaceCase(
                num=num,
                name=name,
                desc=desc,
                func_address=func_address,
                variable=variable,
                times=times,
                project_id=project_id,
                case_set_id=case_set_id,
                user_id=g.userid,
                priority=priority,
                type=case_type,
            )
            db.session.add(m)
            db.session.commit()
            return m.id
        except Exception as e:
            current_app.logger.error(str(e))
            return 102, str(e)
    
    @classmethod
    def case_delete(cls, id):
        try:
            m = InterfaceCase.query.get(id)
            m.status = InterfaceCase.DISABLE
            db.session.add(m)
            db.session.commit()
            return 0
        except Exception as e:
            current_app.logger.error(str(e))
            return 105, str(e)
    
    @classmethod
    def case_modify(cls, id, name, desc, func_address, variable, times, project_id, case_set_id,
                    user_id, priority):
        try:
            m = InterfaceCase.query.get(id)
            m.name = name,
            m.desc = desc,
            m.func_address = func_address,
            m.variable = variable,
            m.times = times,
            m.project_id = project_id,
            m.case_set_id = case_set_id,
            m.modified_user = user_id,
            m.priority = priority
            db.session.add(m)
            db.session.commit()
            return 0, None
        except Exception as e:
            db.session.rollback()
            current_app.logger.error(str(e))
            return 102, str(e)
    
    @classmethod
    def add_case(cls, name, desc, ids, times, case_set_id, func_address, project, variable,
                 api_cases, number, priority, case_type):
        user_id = g.userid
        if not name:
            return jsonify({'msg': '用例名称不能为空', 'status': 0})
        if not case_set_id:
            return jsonify({'msg': '请选择用例集', 'status': 0})
        if re.search(r'\${(.*?)}', '{}{}'.format(variable, json.dumps(api_cases)),
                     flags=0) and not func_address:
            return jsonify({'msg': '参数引用函数后，必须引用函数文件', 'status': 0})
        
        project_data = InterfaceProject.query.filter_by(name=project,
                                                        status=InterfaceProject.ACTIVE).first()
        project_id = project_data.id
        merge_variable = json.dumps(json.loads(variable) + json.loads(project_data.variables))
        _temp_check = extract_variables(convert(json.loads(merge_variable)))
        
        if _temp_check:
            variable_check = []
            for item in _temp_check:
                if item[0]:
                    variable_check.append(item[0])
                else:
                    variable_check.append(item[1])
            return jsonify(
                {'msg': '参数引用${}在业务变量和项目公用变量均没找到'.format(',$'.join(variable_check)), 'status': 0})
        
        try:
            # 合并func 然后去重
            func_address = RunCase.get_case_info_config(func_address, ids)
            all_func_address = []
            for func in func_address:
                if len(func.split('func_list.')) > 1:
                    all_func_address.append(func.split("func_list.")[1] + '.py')
                else:
                    all_func_address.append(func)
            func_address = json.dumps(list(set(all_func_address)))
            cases_check = check_case(api_cases, func_address)
        except Exception as e:
            current_app.logger.info("用例集接口运行的错误信息:" + str(e))
            return jsonify({'msg': '函数或接口参数错误，请检查后提交', 'status': 0})
        
        if cases_check:
            return jsonify({'msg': cases_check, 'status': 0})
        
        variable_check = check_case(variable, func_address)
        if variable_check:
            return jsonify({'msg': variable_check, 'status': 0})
        
        num = auto_num(number, InterfaceCase, project_id=project_id, case_set_id=case_set_id,
                       status=InterfaceCase.ACTIVE)
        
        if ids:
            old_data = InterfaceCase.query.filter_by(id=ids, status=InterfaceCase.ACTIVE).first()
            old_num = old_data.num
            if InterfaceCase.query.filter_by(name=name, project_id=project_id,
                                             case_set_id=case_set_id,
                                             status=InterfaceCase.ACTIVE).first() and name != old_data.name:
                return jsonify({'msg': '用例名字重复', 'status': 0})
            else:
                
                list_data_id = InterfaceCaseSet.query.filter_by(id=case_set_id,
                                                                status=InterfaceCaseSet.ACTIVE).first().id
                list_data = InterfaceCase.query.filter_by(case_set_id=list_data_id,
                                                          status=InterfaceCase.ACTIVE).order_by(
                    asc(InterfaceCase.num)).all()
                num_sort(num, old_num, list_data, old_data)
                
                InterfaceCaseBusiness.case_modify(ids, name, desc, func_address, variable, times,
                                                  project_id, case_set_id, user_id, priority)
            
            for _num, c in enumerate(api_cases):
                if c.get('type') == 'action':
                    # 用例关联表关系
                    if c.get('id'):
                        InterfaceCaseRelationBusiness.update_case_relation(_num, ids, c)
                    else:
                        InterfaceCaseRelationBusiness.create_case_relation(_num, ids, c)
                    continue
                base_encryption = cls.case_encryption(c)
                if c.get('id'):
                    InterfaceCaseDataBusiness.casedata_modify(c.get('id'), _num,
                                                              json.dumps(c['status']),
                                                              c['case_name'],
                                                              c['up_func'], c['down_func'],
                                                              c['time'],
                                                              json.dumps(c['param']),
                                                              json.dumps(c['statusCase']['param']),
                                                              json.dumps(c['header']),
                                                              json.dumps(c['statusCase']['header']),
                                                              json.dumps(c['variable']),
                                                              c['json_variable'],
                                                              json.dumps(
                                                                  c['statusCase']['variable']),
                                                              json.dumps(c['extract']),
                                                              json.dumps(
                                                                  c['statusCase']['extract']),
                                                              json.dumps(c['validate']),
                                                              json.dumps(
                                                                  c['statusCase']['validate']),
                                                              base_encryption, user_id)
                else:
                    InterfaceCaseDataBusiness.casedata_create(_num, json.dumps(c['status']),
                                                              c['case_name'],
                                                              c['up_func'],
                                                              c['down_func'], c['time'],
                                                              json.dumps(c['param']),
                                                              json.dumps(c['statusCase']['param']),
                                                              json.dumps(c['header']),
                                                              json.dumps(c['statusCase']['header']),
                                                              json.dumps(c['variable']),
                                                              c['json_variable'],
                                                              json.dumps(
                                                                  c['statusCase']['variable']),
                                                              json.dumps(c['extract']),
                                                              json.dumps(
                                                                  c['statusCase']['extract']),
                                                              json.dumps(c['validate']),
                                                              json.dumps(
                                                                  c['statusCase']['validate']), ids,
                                                              c['apiMsgId'], base_encryption,
                                                              user_id, case_type)
            return jsonify({'msg': '修改成功', 'status': 1, 'case_id': ids})
        else:
            if InterfaceCase.query.filter_by(name=name, project_id=project_id,
                                             case_set_id=case_set_id,
                                             status=InterfaceCase.ACTIVE).first():
                return jsonify({'msg': '用例名字重复', 'status': 0})
            elif InterfaceCase.query.filter_by(num=num, project_id=project_id,
                                               case_set_id=case_set_id,
                                               status=InterfaceCase.ACTIVE).first():
                return jsonify({'msg': '编号重复', 'status': 0})
            else:
                
                case_id = InterfaceCaseBusiness.case_create(num, name, desc, func_address, variable,
                                                            times, project_id, case_set_id,
                                                            priority, case_type)
                
                for _num, c in enumerate(api_cases):
                    if c.get('type') == 'action':
                        # 用例关联表关系
                        InterfaceCaseRelationBusiness.create_case_relation(_num, case_id, c)
                        continue
                    base_encryption = cls.case_encryption(c)
                    InterfaceCaseDataBusiness.casedata_create(_num, json.dumps(c['status']),
                                                              c['case_name'],
                                                              c['up_func'], c['down_func'],
                                                              c['time'],
                                                              json.dumps(c['param']),
                                                              json.dumps(c['statusCase']['param']),
                                                              json.dumps(c['header']),
                                                              json.dumps(c['statusCase']['header']),
                                                              json.dumps(c['variable']),
                                                              c['json_variable'],
                                                              json.dumps(
                                                                  c['statusCase']['variable']),
                                                              json.dumps(c['extract']),
                                                              json.dumps(
                                                                  c['statusCase']['extract']),
                                                              json.dumps(c['validate']),
                                                              json.dumps(
                                                                  c['statusCase']['validate']),
                                                              case_id, c['apiMsgId'],
                                                              base_encryption, user_id, case_type)
                return jsonify({'msg': '新建成功', 'status': 1, 'case_id': case_id})
    
    @classmethod
    def find_case(cls, project_name, case_name, set_id, page, per_page, case_type):
        
        if not project_name:
            return jsonify({'msg': '请选择项目', 'status': 0})
        project_id = InterfaceProject.query.filter_by(name=project_name,
                                                      status=InterfaceProject.ACTIVE).first().id
        if case_name:
            cases = InterfaceCase.query.filter_by(case_set_id=set_id, status=InterfaceCase.ACTIVE,
                                                  project_id=project_id, type=case_type).filter(
                InterfaceCase.name.like('%{}%'.format(case_name))).all()
            total = len(cases)
            if not cases:
                return jsonify({'msg': '没有该用例', 'status': 0})
        else:
            cases = InterfaceCase.query.filter_by(case_set_id=set_id, status=InterfaceCase.ACTIVE,
                                                  project_id=project_id, type=case_type)
            pagination = cases.order_by(InterfaceCase.num.asc()).paginate(page, per_page=per_page,
                                                                          error_out=False)
            cases = pagination.items
            total = pagination.total
        # 用例信息
        if case_type == 2:
            all_case_name = cls.get_del_case_name(set_id)
            cases = [{'num': c.num, 'name': c.name, 'label': c.name, 'leaf': True, 'desc': c.desc,
                      'sceneId': c.id, 'relation_name': all_case_name[i]}
                     for i, c in enumerate(cases)]
            return jsonify({'data': cases, 'total': total, 'status': 1})
        cases = [{'num': c.num, 'name': c.name, 'label': c.name, 'leaf': True, 'desc': c.desc,
                  'sceneId': c.id} for c in cases]
        return jsonify({'data': cases, 'total': total, 'status': 1})
    
    @classmethod
    def del_case(cls, case_id):
        
        wait_del_case_data = InterfaceCase.query.filter_by(id=case_id,
                                                           status=InterfaceCase.ACTIVE).first()
        # 删除用例信息的关联信息
        if wait_del_case_data.type == 2:
            InterfaceCaseRelationBusiness.del_relation_case(case_id)
        _del_data = InterfaceCaseData.query.filter_by(case_id=case_id,
                                                      execute_status=InterfaceCaseData.ACTIVE).all()
        if _del_data:
            for d in _del_data:
                InterfaceCaseDataBusiness.casedata_delete(d.id)
        
        InterfaceCaseBusiness.case_delete(case_id)
        db.session.delete(wait_del_case_data)
        return jsonify({'msg': '删除成功', 'status': 1})
    
    @classmethod
    def edit_case(cls, case_id, status):
        
        _data = InterfaceCase.query.filter_by(id=case_id, status=InterfaceCase.ACTIVE).first()
        cases = InterfaceCaseData.query.filter_by(case_id=case_id,
                                                  execute_status=InterfaceCaseData.ACTIVE).order_by(
            InterfaceCaseData.num.asc()).all()
        case_relation = InterfaceCaseRelation.query.filter_by(relation_case_id=case_id,
                                                              execute_status=0).all()
        # 如果有关键字
        cases = calculation_case_step_order(cases, case_relation) if case_relation else cases
        case_data = []
        for case in cases:
            # 关键字
            if case.type == 'action':
                case_name = case.case_name
                if not case_name:
                    c_data = InterfaceCase.query.filter_by(id=case.case_id,
                                                           status=0).first()
                    if not c_data:
                        continue
                    case_name = c_data.desc
                case_data.append(
                    {
                        'num': case.num,
                        'name': InterfaceCase.query.filter_by(id=case.case_id,
                                                              status=0).first().name,
                        'case_name': case_name,
                        'case_id': case.case_id,
                        'time': case.times,
                        'id': '' if status else case.id,
                        'type': case.type,
                        'status': json.loads(case.status),
                    })
            else:
                case_data.append(
                    {
                        'num': case.num,
                        'name': InterfaceApiMsg.query.filter_by(id=case.api_msg_id,
                                                                status=InterfaceApiMsg.ACTIVE).first().name,
                        'desc': InterfaceApiMsg.query.filter_by(id=case.api_msg_id,
                                                                status=InterfaceApiMsg.ACTIVE).first().desc,
                        'apiMsgId': case.api_msg_id,
                        'id': '' if status else case.id,
                        'status': json.loads(case.status),
                        'variableType': InterfaceApiMsg.query.filter_by(id=case.api_msg_id,
                                                                        status=InterfaceApiMsg.ACTIVE).first().variable_type,
                        'case_name': case.name,
                        'time': case.time,
                        'up_func': case.up_func,
                        'down_func': case.down_func,
                        'header': json.loads(case.header),
                        'variable': json.loads(case.variable),
                        'json_variable': case.json_variable,
                        'param': json.loads(case.param),
                        'extract': json.loads(case.extract),
                        'validate': json.loads(case.validate),
                        'statusCase': {
                            'header': json.loads(case.status_header),
                            'variable': json.loads(case.status_variables),
                            'extract': json.loads(case.status_extract),
                            'validate': json.loads(case.status_validate),
                            'param': json.loads(case.status_param)
                        },
                    })
        _data2 = {
            'num': _data.num, 'name': _data.name, 'desc': _data.desc, 'cases': case_data,
            'priority': _data.priority,
            'setId': _data.case_set_id,
            'func_address': json.loads(_data.func_address), 'times': _data.times
        }
        if _data.variable:
            _data2['variable'] = json.loads(_data.variable)
        else:
            _data2['variable'] = []
        return jsonify({'data': _data2, 'status': 1})
    
    @classmethod
    def case_encryption(cls, api_case):
        """用例加密
        :param api_case:用例内容
        :return:md5加密32位
        """
        e_header = api_case.get('header')
        e_variable = api_case.get('variable')
        e_param = api_case.get('param')
        e_header = InterfaceApiMsgBusiness.get_extract_header_variable(e_header)
        e_variable = InterfaceApiMsgBusiness.get_extract_header_variable(e_variable)
        e_param = InterfaceApiMsgBusiness.get_extract_header_variable(e_param)
        msg_encryption = {
            'header': e_header,
            'variable': e_variable,
            'param': e_param,
        }
        if api_case['json_variable']:
            msg_encryption['json_variable'] = api_case['json_variable']
        current_app.logger.info(msg_encryption)
        # md5加密
        json.dumps(msg_encryption)
        base_encryption = parse_pwd(str(msg_encryption))
        
        return base_encryption
    
    @classmethod
    def get_del_case_name(cls, set_id):
        """获取删除用例信息关联的接口用例
        :param set_id:用例信息模块id
        :return: all_case_name
        """
        all_case_name = []
        case_data = InterfaceCase.query.filter_by(case_set_id=set_id, status=0).all()
        if case_data:
            # 遍历用例信息
            for c_data in case_data:
                case_ids = InterfaceCaseRelation.query.filter_by(case_id=c_data.id,
                                                                 execute_status=0).all()
                relation_case_id = list(
                    set([c.relation_case_id for c in case_ids if case_ids]))
                if not relation_case_id:
                    all_case_name.append(relation_case_id)
                    continue
                all_case = [
                    InterfaceCase.query.filter_by(id=case_id, status=0).first()
                    for case_id in relation_case_id]
                base_case_name = [case_name.name for case_name in all_case if case_name]
                all_case_name.append(base_case_name)
        return all_case_name
